package org.hive.testserver.domain.model.schedule;

import java.io.Serializable;
import java.util.List;

import org.apache.commons.lang.SerializationUtils;
import org.hive.testserver.domain.model.Slave;
import org.hive.testserver.domain.model.Test;
import org.hive.testserver.domain.model.Work;
import org.hive.testserver.domain.model.WorkSheet;

/**
 * When rerunning tests we prefer that the test is executed on an alternative slave to any
 * it has already been executed on. Do not return any ScheduleTests that are being
 * rerun on the same slave that could be run elsewhere.
 */
public class PreferAlternativeSlaveForRerunAllocator implements AllocatorLink, Serializable {

	private AllocatorLink next;
	
	private static final long serialVersionUID = 1L;
	
	public PreferAlternativeSlaveForRerunAllocator(AllocatorLink next) {
		super();
		this.next = next;
	}


	public boolean accept(Test test, Slave slave, WorkSheet workSheet) {
		return next.accept(test, slave, workSheet);
	}


	protected boolean shouldExecuteOnAlternativeSlave(Test test, Slave slave, WorkSheet workSheet) {
		return testHasAlreadyBeenExecutedBySlave(test, slave, workSheet) && workSheet.areThereAnyOtherActiveSlaves(slave);
	}

	private boolean testHasAlreadyBeenExecutedBySlave(Test test, Slave slave, WorkSheet workSheet) {
		if (test.timesExecuted() == 0) {
			return false;
		}
		List<Work> works = workSheet.findWorksForTest(test);
		for (Work work : works) {
			if (slave.equals(work.assignedSlave())) {
				return true;
			}
		}
		return false;
	}

	public AllocatorLink createWorkingCopy() {
		return (AllocatorLink) SerializationUtils.clone(this);
	}


	public ScheduledTest scheduleNextTest(Slave slave, WorkSheet workSheet) {
		while (true) {
			ScheduledTest scheduledTest = next.scheduleNextTest(slave, workSheet);
			Test test = scheduledTest.testOrNull();
			if (test != null) {
				if (! shouldExecuteOnAlternativeSlave(test, slave, workSheet)) {
					return scheduledTest;
				}
			} else {
				return scheduledTest;
			}
		}
	}


	public boolean shouldConsiderTestFinished(Test test) {
		return next.shouldConsiderTestFinished(test);
	}

}
